﻿using System;
using System.Linq;
using CID.Domain.Exceptions;
using CID.Domain.Infrastructure;
using CID.Tests.Support;
using NUnit.Framework;

namespace CID.Domain.Tests.Infrastructure
{
    [TestFixture]
    class TemplateTests : BaseFunctionalTests
    {
        #region Template Concrete Implementation
        class Template : CID.Domain.Infrastructure.Template
        {
            public override ITemplate CreateCopy()
            {
                return new Template();
            }
        }
        #endregion

        private const string Name = "Template Name";
        private const string Description = "Template Description";
        private static readonly byte[] Content = new byte[] { 1, 2, 3 };
        private static readonly ITemplateType Type = StubsBuilder.BuildTemplateTypeStub();

        private static void AssertNewTemplate(ITemplate template)
        {
            Assert.AreEqual(Type, template.Type);
            Assert.AreEqual(Name, template.Name);
            Assert.AreEqual(Description, template.Description);
            Assert.AreEqual(Content, template.Content);
            Assert.IsTrue(template.Enabled);
            Assert.IsFalse(template.MultipleServersSupport);
        }

        private static void AssertIsCopy(ITemplate original, ITemplate copy)
        {
            Assert.AreEqual(String.Empty, copy.Name);
            Assert.AreEqual(String.Empty, copy.Description);
            Assert.AreEqual(original.GetType(), copy.GetType());
            Assert.AreEqual(original.Type, copy.Type);
            Assert.AreEqual(original.Content, copy.Content);
            Assert.AreEqual(original.Enabled, copy.Enabled);
            Assert.AreEqual(original.MultipleServersSupport, copy.MultipleServersSupport);
            CollectionAssert.AreEquivalent(original.Parameters.Select(x => new { x.Name, x.Description, x.Type }).ToArray(), copy.Parameters.Select(x => new { x.Name, x.Description, x.Type }).ToArray());
        }

        private Template template;

        public override void SetUp()
        {
            base.SetUp();
            template = new Template();
        }

        #region Constructor
        [Test]
        public void CreateDeploymentTemplate_ShouldSucceed()
        {
            var deploymentTemplate = new DeploymentTemplate(Type, Name, Description, Content);

            AssertNewTemplate(deploymentTemplate);
        }
        
        [Test]
        public void CreateActivationTemplate_ShouldSucceed()
        {
            var activationTemplate = new ActivationTemplate(Type, Name, Description, Content);

            AssertNewTemplate(activationTemplate);
        }

        [Test]
        public void CreateDeactivationTemplate_ShouldSucceed()
        {
            var deactivationTemplate = new DeactivationTemplate(Type, Name, Description, Content);

            AssertNewTemplate(deactivationTemplate);
        }
        #endregion

        #region CreateCopy
        private static void AddParametersFor(ITemplate template)
        {
            template.CreateParameter("First Parameter", "The first parameter", ParameterType.Boolean);
            template.CreateParameter("Second Parameter", "The second parameter", ParameterType.DateTime);
            template.CreateParameter("Third Parameter", "The third parameter", ParameterType.Decimal);
            template.CreateParameter("Fourth Parameter", "The fourth parameter", ParameterType.Integer);
            template.CreateParameter("Fifth Parameter", "The fifth parameter", ParameterType.String);
        }
        [Test]
        public void CreateCopy_ForDeploymentTemplate_ShouldSucceed()
        {
            var deploymentTemplate = new DeploymentTemplate(Type, Name, Description, Content);
            AddParametersFor(deploymentTemplate);

            var copy = deploymentTemplate.CreateCopy();
            
            AssertIsCopy(deploymentTemplate, copy);
        }

        [Test]
        public void CreateCopy_ForActivationTemplate_ShouldSucceed()
        {
            var activationTemplate = new ActivationTemplate(Type, Name, Description, Content);
            AddParametersFor(activationTemplate);

            var copy = activationTemplate.CreateCopy();

            AssertIsCopy(activationTemplate, copy);
        }

        [Test]
        public void CreateCopy_ForDeactivationTemplate_ShouldSucceed()
        {
            var deactivationTemplate = new DeactivationTemplate(Type, Name, Description, Content);
            AddParametersFor(deactivationTemplate);

            var copy = deactivationTemplate.CreateCopy();

            AssertIsCopy(deactivationTemplate, copy);
        }
        #endregion

        #region CreateParameter
        private const string Parameter1Name = "Parameter One Name";
        private const string Parameter1Description = "Parameter One Description";
        private const ParameterType Parameter1Type = ParameterType.Decimal;
        private const string Parameter2Name = "Parameter Two Name";
        private const string Parameter2Description = "Parameter Two Description";
        private const ParameterType Parameter2Type = ParameterType.Boolean;

        [Test]
        public void CreateParameter_ParameterNameNotAssignedYet_ShouldSucceed()
        { 
            var parameter = template.CreateParameter(Parameter1Name, Parameter1Description, Parameter1Type);

            Assert.IsInstanceOf<Parameter>(parameter);
            Assert.AreEqual(Parameter1Name, parameter.Name);
            Assert.AreEqual(Parameter1Description, parameter.Description);
            Assert.AreEqual(Parameter1Type, parameter.Type);
            Assert.AreEqual(template, parameter.Template);
            Assert.AreEqual(1, template.Parameters.Count());
            CollectionAssert.Contains(template.Parameters, parameter);
        }

        [Test, ExpectedException(typeof(DuplicateChildException))]
        public void CreateParameter_ParameterNameAlreadyAssigned_ShouldThrowException()
        {
            var parameter1 = template.CreateParameter(Parameter1Name, Parameter1Description, Parameter1Type);
            var parameter2 = template.CreateParameter(Parameter1Name, Parameter2Description, Parameter2Type);
        }
        #endregion

        #region RemoveParameter
        [Test]
        public void RemoveParameter_ParameterPreviouslyAdded_ShouldSucceed()
        {
            var parameter1 = template.CreateParameter(Parameter1Name, Parameter1Description, Parameter1Type);
            var parameter2 = template.CreateParameter(Parameter2Name, Parameter2Description, Parameter2Type);

            template.RemoveParameter(parameter1);

            Assert.AreEqual(1, template.Parameters.Count());
            CollectionAssert.Contains(template.Parameters, parameter2);
            Assert.AreEqual(template, parameter2.Template);
            CollectionAssert.DoesNotContain(template.Parameters, parameter1);
            Assert.IsNull(parameter1.Template);
        }

        [Test]
        public void RemoveParameter_ParameterNotAdded_NothingShouldHappen()
        {
            var parameter1 = StubsBuilder.BuildParameterStub();
            var parameter2 = template.CreateParameter(Parameter2Name, Parameter2Description, Parameter2Type);

            template.RemoveParameter(parameter1);

            Assert.AreEqual(1, template.Parameters.Count());
            CollectionAssert.Contains(template.Parameters, parameter2);
            CollectionAssert.DoesNotContain(template.Parameters, parameter1);
        }

        [Test]
        public void RemoveParameter_ParameterFromAnotherTemplate_NothingShouldHappen()
        {
            var anotherTemplate = new Template();
            var parameter1 = anotherTemplate.CreateParameter(Parameter1Name, Parameter1Description, Parameter1Type);
            var parameter2 = template.CreateParameter(Parameter2Name, Parameter2Description, Parameter2Type);

            template.RemoveParameter(parameter1);

            Assert.AreEqual(1, template.Parameters.Count());
            CollectionAssert.Contains(template.Parameters, parameter2);
            Assert.AreEqual(template, parameter2.Template);
            CollectionAssert.Contains(anotherTemplate.Parameters, parameter1);
            Assert.AreEqual(anotherTemplate, parameter1.Template);
        }
        #endregion
    }
}
